﻿using Microsoft.AspNetCore.Hosting.Server;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _4_NovaAdmin.Web.Core.IM
{
    public class ImClient
    {
        private readonly ImUtility _imUtility;
        public readonly StackExchange.Redis.IDatabase _redis;
        public StackExchange.Redis.ISubscriber _subscriber;
        public ImClient(ImUtility imUtility)
        {
            _imUtility = imUtility;
            _redis = imUtility._redis;
            _subscriber = imUtility._subscriber;
        }


        /// <summary>
        /// ImServer 连接前的负载、授权，返回 ws 目标地址，使用该地址连接 websocket 服务端
        /// </summary>
        /// <param name="clientId">客户端id</param>
        /// <param name="clientMetaData">客户端相关信息，比如ip</param>
        /// <returns>websocket 地址：ws://xxxx/ws?token=xxx</returns>
        public async Task<string> PrevConnectServer(Guid clientId, string clientMetaData)
        {
            var server = _imUtility.SelectServer(clientId);
            var token = $"{Guid.NewGuid()}{Guid.NewGuid()}{Guid.NewGuid()}{Guid.NewGuid()}".Replace("-", "");
            await _redis.StringSetAsync($"{_imUtility._redisPrefix}Token{token}", JsonConvert.SerializeObject((clientId, clientMetaData)), TimeSpan.FromSeconds(10));
            return $"ws://{server}{_imUtility._pathMatch}?token={token}";
        }

        /// <summary>
        /// 判断客户端是否在线
        /// </summary>
        /// <param name="clientId"></param>
        /// <returns></returns>
        public bool HasOnline(Guid clientId)
        {
            // 获取哈希表中的值
            var value = _redis.HashGet($"{_imUtility._redisPrefix}Online", clientId.ToString());

            // 转换为int并比较
            return int.TryParse(value, out int onlineStatus) && onlineStatus > 0;
        }

        #region 群聊频道，每次上线都必须重新加入

        /// <summary>
        /// 加入群聊频道，每次上线都必须重新加入
        /// </summary>
        /// <param name="clientId">客户端id</param>
        /// <param name="chan">群聊频道名</param>
        public void JoinChan(Guid clientId, string chan)
        {
            // 创建管道批量操作
            var batch = _redis.CreateBatch();

            // 构建三个哈希表操作
            batch.HashSetAsync($"{_imUtility._redisPrefix}Chan{chan}", clientId.ToString(), 0);
            batch.HashSetAsync($"{_imUtility._redisPrefix}Client{clientId}", chan, 0);
            batch.HashIncrementAsync($"{_imUtility._redisPrefix}ListChan", chan, 1);

            // 执行管道中的所有命令
            batch.Execute();
        }

        /// <summary>
        /// 获取群聊频道所有客户端id（测试）
        /// </summary>
        /// <param name="chan">群聊频道名</param>
        /// <returns></returns>
        public Guid[] GetChanClientList(string chan)
        {
            // 获取哈希表中的所有键
            RedisValue[] keys = _redis.HashKeys($"{_imUtility._redisPrefix}Chan{chan}");

            // 将键转换为 Guid
            return keys.Select(key => Guid.Parse(key)).ToArray();
        }

        /// <summary>
        /// 清理群聊频道的离线客户端（测试）
        /// </summary>
        /// <param name="chan">群聊频道名</param>
        public void ClearChanClient(string chan)
        {
            // 获取群聊频道中的所有客户端ID
            RedisValue[] websocketIds = _redis.HashKeys($"{_imUtility._redisPrefix}Chan{chan}");
            var offline = new List<RedisValue>();

            // 每次处理10个客户端ID
            int batchSize = 10;
            for (int start = 0; start < websocketIds.Length; start += batchSize)
            {
                int length = Math.Min(batchSize, websocketIds.Length - start);
                RedisValue[] slice = websocketIds.Skip(start).Take(length).ToArray();

                // 检查这些客户端是否在线
                RedisValue[] hvals = _redis.HashGet($"{_imUtility._redisPrefix}Online", slice);

                // 找出离线的客户端
                for (int i = 0; i < hvals.Length; i++)
                {
                    if (string.IsNullOrEmpty(hvals[i]))
                    {
                        offline.Add(slice[i]);
                    }
                }
            }

            // 删除离线的客户端
            if (offline.Any())
            {
                _redis.HashDelete($"{_imUtility._redisPrefix}Chan{chan}", offline.ToArray());
            }
        }

        /// <summary>
        /// 获取所有群聊频道和在线人数
        /// </summary>
        /// <returns>频道名和在线人数</returns>
        public IEnumerable<(string chan, long online)> GetChanList()
        {
            var listChanKey = $"{_imUtility._redisPrefix}ListChan";

            // 1. 获取哈希表所有字段和值
            HashEntry[] entries = _redis.HashGetAll(listChanKey);

            // 2. 转换为元组集合（含类型转换校验）
            return entries.Select(e =>
            {
                if (long.TryParse(e.Value.ToString(), out var count))
                    return (chan: e.Name.ToString(), online: count);

                throw new FormatException($"无效的在线人数数值: {e.Value} (频道: {e.Name})");
            });
        }

        /// <summary>
        /// 获取群聊频道的在线人数
        /// </summary>
        /// <param name="chan">群聊频道名</param>
        /// <returns>在线人数</returns>
        public long GetChanOnline(string chan)
        {
            var listChanKey = $"{_imUtility._redisPrefix}ListChan";

            // 1. 获取哈希表字段值（对应HGET命令）
            RedisValue onlineCount = _redis.HashGet(listChanKey, chan);

            // 2. 安全类型转换（含空值处理）
            if (onlineCount.IsNull) return 0;  // 频道不存在时返回

            if (long.TryParse(onlineCount.ToString(), out var count))
                return count;

            throw new FormatException($"无效的在线人数数值: {onlineCount} (频道: {chan})");
        }

        /// <summary>
        /// 发送群聊消息，所有在线的用户将收到消息
        /// </summary>
        /// <param name="senderClientId">发送者的客户端id</param>
        /// <param name="chan">群聊频道名</param>
        /// <param name="message">消息</param>
        public void SendChanMessage(Guid senderClientId, string chan, object message)
        {
            // 获取群聊频道中的所有客户端ID
            RedisValue[] websocketIds = _redis.HashKeys($"{_imUtility._redisPrefix}Chan{chan}");

            // 过滤出有效的客户端ID
            Guid[] clientIds = websocketIds
                .Where(id => !string.IsNullOrEmpty(id))
                .Select(id => Guid.TryParse(id, out Guid tryUuid) ? tryUuid : Guid.Empty)
                .Where(id => id != Guid.Empty)
                .ToArray();

            // 调用 SendMessage 方法发送消息
            _imUtility.SendMessage(senderClientId, clientIds, message);
        }

        #endregion
    }
}
